package geometry;

/**
 * Represents a mathematical vector in 3D space.  Vectors are defined by a direction and a magnitude.  Should the length be negative, the Vector is considered to be facing the opposite direction in the same magnitude.
 *
 * @author Catastrophe
 */
public final class Vector3D
{
	/**
	 * The Point3D at the head of this Vector3D assuming the tail is at the origin.
	 */
	private Point3D head;

	/**
	 * @return a unit vector in the direction of the positive X axis.
	 */
	public static Vector3D xAxis()
	{
		return new Vector3D(new Point3D(1.0, 0.0, 0.0));
	}

	/**
	 * @return a unit vector in the direction of the positive Y axis.
	 */
	public static Vector3D yAxis()
	{
		return new Vector3D(new Point3D(0.0, 1.0, 0.0));
	}

	/**
	 * @return a unit vector in the direction of the positive Z axis.
	 */
	public static Vector3D zAxis()
	{
		return new Vector3D(new Point3D(0.0, 0.0, 1.0));
	}

	/**
	 * Creates a new Vector3D.
	 *
	 * @param head the Point3D marking the head of the Vector assuming the tail is at the origin.
	 * @throws NullPointerException if the specified Point3D is <code>null</code>.
	 */
	public Vector3D(Point3D head) throws NullPointerException
	{
		if(head == null)
			throw new NullPointerException();
		this.head = head;
	}

	/**
	 * Creates a new Vector3D.
	 *
	 * @param head the Point3D marking the head of the Vector.
	 * @param tail the Point3D marking the tail of the Vector.
	 * @throws NullPointerException if either of the specified Point3D is <code>null</code>.
	 */
	public Vector3D(Point3D tail, Point3D head) throws NullPointerException
	{
		if(head == null || tail == null)
			throw new NullPointerException();
		this.head = new Point3D(head.getX() - tail.getX(), head.getY() - tail.getY(), head.getZ() - tail.getZ());
	}

	/**
	 * Creates a new Vector3D.
	 *
	 * @param horizontal the angle between the positive X axis and the Vector3D along the XY plane.
	 * @param vertical the angle between the positive X axis and the Vector3D along the XZ plane.
	 * @param magnitude the magnitude of the new Vector3D.
	 * @throws NullPointerException if either specified angle is <code>null</code>.
	 */
	public Vector3D(Angle horizontal, Angle vertical, double magnitude) throws NullPointerException
	{
		if(horizontal == null || vertical == null)
			throw new NullPointerException();
		double x = horizontal.cos() * vertical.cos() * magnitude;
		double y = horizontal.sin() * magnitude;
		double z = vertical.sin() * magnitude;
		head = new Point3D(x, y, z);
	}

	/**
	 * @return the head of this Vector3D assuming its tail is at the origin.
	 */
	public Point3D head()
	{
		return head;
	}

	/**
	 * @return the magnitude of this Vector.
	 */
	public double magnitude()
	{
		return head.distanceTo(Point3D.origin());
	}

	/**
	 * Changes the direction of this Vector to point in the opposite direction.  The length remains unchanged.
	 */
	public void invert()
	{
		head.invert();
	}

	/**
	 * Multiplies this Vector by a scalar value.
	 *
	 * @param multiplier the scalar multiplier.
	 */
	public void scalarMultiply(double multiplier)
	{
		head = Utility.multiple(head, multiplier);
	}
}